package com.aipsi.cws.auto.common;

import com.appium.manager.element.anno.PageElement;
import io.appium.java_client.TouchAction;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.AndroidElement;
import io.appium.java_client.touch.WaitOptions;
import io.appium.java_client.touch.offset.PointOption;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.ProtocolException;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.UnreachableBrowserException;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;


import java.io.FileNotFoundException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author 大忽悠
 * @create 2022/7/8 17:00
 */
@Slf4j
public class BasePage {
    protected PageElement pageElement;
    private Class<?> curTestClass;

    public BasePage(PageElement pageElement, Class<?> curTestClass) {
        this.pageElement = pageElement;
        this.curTestClass = curTestClass;
        pageElement.initPage(this);
    }

    public AndroidDriver<WebElement> getDriver(){
        return pageElement.getAndroidDriver();
    }

    //-------------------------测试数据写入--------------------------

    /**
     * 当需要自动写入测试数据时,需要在写入前,设置好当前正在执行的测试方法
     */
    public BasePage setCurTestMethod(Method curTestMethod) {
        pageElement.setTestMethod(curTestMethod);
        return this;
    }

    public BasePage setCurTestMethod(String curTestMethodName) throws NoSuchMethodException {
        setCurTestMethod(curTestClass.getMethod(curTestMethodName));
        return this;
    }

    //----元素是单个类型-----

    /**
     * 输入测试数据
     * 需要输入的元素集合
     */
    public BasePage singleInput(String curTestMethodName, String... elementsName) throws IllegalAccessException, FileNotFoundException, NoSuchMethodException {
        setCurTestMethod(curTestMethodName);
        return singleInput(elementsName);
    }

    /**
     * 调用该方法前一定要先setCurTestMethod
     */
    private BasePage singleInput(String[] elementsName) throws IllegalAccessException, FileNotFoundException {
        WebElement type=new AndroidElement();
        List<WebElement> targetElements = foundTargetElements(elementsName,type,WebElement.class);
        for (WebElement targetElement : targetElements) {
            pageElement.write(targetElement);
        }
        return this;
    }

    //----元素是集合类型,需要选用集合中某个特点位置元素进行数据输入-----

    public BasePage listInput(String curTestMethodName,String... elementsName) throws IllegalAccessException, FileNotFoundException, NoSuchMethodException {
        return listInput(curTestMethodName,0,elementsName);
    }

    public BasePage listInput(String curTestMethodName,int index,String... elementsName) throws IllegalAccessException, FileNotFoundException, NoSuchMethodException {
        setCurTestMethod(curTestMethodName);
        return listInput(index, elementsName);
    }

    /**
     * 使用前提是设置了setCurTestMethod
     */
    public BasePage listInput(String... elementsName) throws IllegalAccessException, FileNotFoundException, NoSuchMethodException {
        return listInput(0,elementsName);
    }

    /**
     * 使用前提是设置了setCurTestMethod
     */
    private BasePage listInput(int index, String[] elementsName) throws IllegalAccessException, FileNotFoundException {
        List<WebElement> type=new ArrayList<>();
        List<List<WebElement>> targetElements = foundTargetElements(elementsName,type,List.class);
        if(index >=targetElements.size()){
            throw new IllegalAccessException("当前元素只找到了"+targetElements.size()+"个,但是传入的index却是"+ index);
        }
        for (List<WebElement> targetElement : targetElements) {
            pageElement.write(targetElement.get(index));
        }
        return this;
    }

    private <T> List<T> foundTargetElements(String[] elementsName,T type,Class<?> typeClass) throws IllegalAccessException {
        List<String> elements = Arrays.asList(elementsName);
        List<T> targetElements = new ArrayList<>();
        for (Field field : this.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            if (elements.contains(field.getName())&&field.getType().equals(typeClass)) {
                targetElements.add((T) field.get(this));
            }
        }
        if(targetElements.isEmpty()){
           throw new RuntimeException("未找到符合["+elementsName+"]要求的相关元素");
        }
        return targetElements;
    }

    //----------------------等待-----------------------------------

    public BasePage waitWhile() throws InterruptedException {
        log.info("等待3s");
        pageElement.waitWhile();
        return this;
    }

    /**
     * 单位是s
     */
    public BasePage waitWhile(int waitTime) throws InterruptedException {
        log.info("等待{}s", waitTime);
        pageElement.waitWhile(waitTime * 1000);
        return this;
    }

    //--------------------------滑动操作------------------------------------------


    /**
     * 滑动耗时默认为300毫秒
     */
    public void swipe(int startPointX, int startPointY, int endPointX, int endPointY){
        swipe(startPointX,startPointY,endPointX,endPointY,1000);
    }

    /**
     * @param startPointX 滑动起始坐标x
     * @param startPointY  滑动起始坐标y
     * @param endPointX    滑动结束坐标x
     * @param endPointY    滑动结束坐标y
     * @param duration      滑动耗时--默认毫秒
     */
    public void swipe(int startPointX, int startPointY, int endPointX, int endPointY, int duration){
        TouchAction touchAction = new TouchAction(getDriver());
        touchAction.press(PointOption.point(startPointX,startPointY))
                .waitAction(WaitOptions.waitOptions(Duration.ofMillis(duration)))
                .moveTo(PointOption.point(endPointX,endPointY)).release().perform();
    }

    //------------------------坐标点击------------------------------------

    public void clickByCoordinate(int x,int y){
        TouchAction touchAction = new TouchAction(getDriver());
        touchAction.tap(PointOption.point(x,y)).perform();
    }

    //----------------------获取某个元素的value属性值-----------------------------------


    /**
     * 获取某个元素的value属性值
     */
    public String getSingleElementValue(String elementVal) throws NoSuchFieldException, IllegalAccessException {
        Field element = this.getClass().getDeclaredField(elementVal);
        element.setAccessible(true);
        if(element.getType().equals(List.class)){
            return ((List<WebElement>)element.get(this)).get(0).getAttribute("value");
        }
        return ((WebElement)element.get(this)).getAttribute("value");
    }

    public String getListElementValue(String elementVal,int index) throws NoSuchFieldException, IllegalAccessException {
        Field element = this.getClass().getDeclaredField(elementVal);
        element.setAccessible(true);
        if(element.getType().equals(List.class)){
            return ((List<WebElement>)element.get(this)).get(index).getAttribute("value");
        }
        return ((WebElement)element.get(this)).getAttribute("value");
    }

    //---------------------判断操作---------------------------------

    /**
     * 判断元素是否可以被点击,默认搜索时间为3s,每隔1s搜索一次,如果判断条件未生效,则抛出异常
     */
    public Boolean canClicked(WebElement webElement){
        return canClicked(webElement,3,1);
    }

    public Boolean canClicked(WebElement webElement,int searchTotalTime,int searchPerTime){
        WebDriverWait wait = createWebDriverWait(searchTotalTime,searchPerTime);
        try{
            WebElement element = wait.until(ExpectedConditions.elementToBeClickable(webElement));
            element.click();
        }catch (Exception e){
            return false;
        }
        return true;
    }

    /**
     * 搜寻的总时间和每隔多长时间搜索一次
     */
    public WebDriverWait createWebDriverWait(int searchTotalTime,int searchPerTime) {
        return (WebDriverWait) new WebDriverWait(pageElement.getAndroidDriver(),
                Duration.ofSeconds(searchTotalTime).getSeconds(), Duration.ofSeconds(searchPerTime).getSeconds())
                .ignoring(NoSuchElementException.class)
                .ignoring(WebDriverException.class)
                .ignoring(UnreachableBrowserException.class)
                .ignoring(ProtocolException.class);
    }
}
